using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

namespace VirtualScreenMaximizer
{

    public class MockScreen
    {

        private int bitsPerPixel;
        public int BitsPerPixel
        {
            get { return bitsPerPixel; }
        }

        private string deviceName;
        public string DeviceName
        {
            get { return deviceName; }
        }

        private Rectangle workingArea;
        public Rectangle WorkingArea
        {
            get { return workingArea; }
        }

        private bool primary;
        public bool Primary
        {
            get { return primary; }
        }

        private Rectangle bounds = Rectangle.Empty;
        public Rectangle Bounds
        {
            get { return bounds; }
        }

        bool horizontal = false;
        public bool Horizontal
        {
            get { return horizontal; }
            set { horizontal = value; }
        }

        bool vertical = false;
        public bool Vertical
        {
            get { return vertical; }
            set { vertical = value; }
        }

        bool incomplete = true;
        public bool Incomplete
        {
            get { return incomplete; }
            set { incomplete = value; }
        }

        public MockScreen(string name)
        {
            this.deviceName = name;
            incomplete = true;
        }

        public MockScreen(Screen s)
        {
            this.bitsPerPixel = s.BitsPerPixel;
            this.bounds = s.Bounds;

            this.deviceName = Maximizer.neuterDeviceName(s.DeviceName.Substring(0, 12));
            this.primary = s.Primary;
            this.workingArea = s.WorkingArea;
            incomplete = false;
        }
        public MockScreen(int bpp, Rectangle bounds, string name, bool primary, Rectangle warea)
        {
            this.bitsPerPixel = bpp;
            this.bounds = bounds;
            this.deviceName = name;
            this.primary = primary;
            this.workingArea = warea;
            incomplete = false;
        }
    }
    public class Maximizer
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        public static uint MOD_ALT = 0x1;
        public static uint MOD_CONTROL = 0x2;
        public static uint MOD_SHIFT = 0x4;
        public static uint MOD_WIN = 0x8;
        public static uint WM_HOTKEY = 0x312;



        public static string neuterDeviceName(string s)
        {
            return s.Split(new char[1] { (char)0 }, 2)[0];
        }




        public static Dictionary<int, string> keyAssignments = new Dictionary<int, string>();

        public static void RegisterHK(IntPtr handle, int hashCode, System.Windows.Forms.Keys key, uint modifiers, string command)
        {
            if (keyAssignments.ContainsKey(hashCode + (int)key))
            {
                return;
            }

            keyAssignments.Add(hashCode + (int)key, command);
            RegisterHotKey(handle, hashCode + (int)key, modifiers, (uint)key);

        }
        public static void UnRegisterHK(IntPtr handle, int hashCode, System.Windows.Forms.Keys key)
        {
            if (!keyAssignments.ContainsKey(hashCode + (int)key))
            {
                return;
            }
            keyAssignments.Remove(hashCode + (int)key);
            UnregisterHotKey(handle, hashCode + (int)key);
        }



        public static void ProcessHK(IntPtr wParam, ConfiguredMockScreens mockScreens)
        {

            if (keyAssignments.ContainsKey(wParam.ToInt32()))
            {
                string command = keyAssignments[wParam.ToInt32()];
                //exec wparam
                Console.WriteLine("HotKey {0}", keyAssignments[wParam.ToInt32()]);
                //FullScreen
                switch (command)
                {

                    case "FullScreen":
                        Maximizer.FullScreen(mockScreens);
                        break;
                    case "RestoreOriginal":
                        Maximizer.RestoreOriginal();
                        Maximizer.RestoreOriginal();
                        break;
                }

            }

        }

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll", SetLastError = true)]
        static extern int GetWindowLong(IntPtr hWnd, int nIndex);
        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
        [DllImport("user32.dll")]
        static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);


        static readonly int GWL_STYLE = (-16);
        /*static readonly int GWL_WNDPROC = (-4);
        static readonly int GWL_HINSTANCE = (-6);
        static readonly int GWL_HWNDPARENT = (-8);
		
        static readonly int GWL_EXSTYLE = (-20);
        static readonly int GWL_USERDATA = (-21);
        static readonly int GWL_ID = (-12);

        static readonly uint GW_HWNDFIRST = 0;
        static readonly uint GW_HWNDLAST = 1;
        static readonly uint GW_HWNDNEXT = 2;
        static readonly uint GW_HWNDPREV = 3;
        static readonly uint GW_OWNER = 4;
        static readonly uint GW_CHILD = 5;
        static readonly uint GW_ENABLEDPOPUP = 6;
        static readonly uint GW_MAX = 6;*/
        static readonly uint WS_MAXIMIZEBOX = 0x10000;


        public struct ConfiguredKey
        {
            public bool Alt;
            public bool Control;
            public bool Shift;
            public int KeyCode;
            public ConfiguredKey(bool a, bool c, bool s, int kc)
            {
                this.Alt = a;
                this.Control = c;
                this.Shift = s;
                this.KeyCode = kc;
            }
        }


        [StructLayout(LayoutKind.Sequential)]
        public struct WINDOWPLACEMENT
        {
            public int length;
            public int flags;
            public int showCmd;
            public System.Drawing.Point ptMinPosition;
            public System.Drawing.Point ptMaxPosition;
            public System.Drawing.Rectangle rcNormalPosition;

            public static WINDOWPLACEMENT Default
            {
                get
                {
                    WINDOWPLACEMENT result = new WINDOWPLACEMENT();
                    result.length = Marshal.SizeOf(result);
                    return result;
                }
            }
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
        [DllImport("user32.dll")]
        static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);





        private static Dictionary<IntPtr, int> originalWinLongs = new Dictionary<IntPtr, int>();
        private static Dictionary<IntPtr, int> originalWinLongs2 = new Dictionary<IntPtr, int>();
        private static Dictionary<IntPtr, WINDOWPLACEMENT> originalWindowPlacements = new Dictionary<IntPtr, WINDOWPLACEMENT>();

        const int SW_HIDE = 0;
        const int SW_SHOWNORMAL = 1;
        const int SW_NORMAL = 1;
        const int SW_SHOWMINIMIZED = 2;
        const int SW_SHOWMAXIMIZED = 3;
        const int SW_MAXIMIZE = 3;
        const int SW_SHOWNOACTIVATE = 4;
        const int SW_SHOW = 5;
        const int SW_MINIMIZE = 6;
        const int SW_SHOWMINNOACTIVE = 7;
        const int SW_SHOWNA = 8;
        const int SW_RESTORE = 9;
        const int SW_SHOWDEFAULT = 10;
        const int SW_FORCEMINIMIZE = 11;
        const int SW_MAX = 11;

        const int GWL_EXSTYLE = (-20);

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);




        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);
        [Serializable, StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;

            public RECT(int left_, int top_, int right_, int bottom_)
            {
                Left = left_;
                Top = top_;
                Right = right_;
                Bottom = bottom_;
            }

            public int Height { get { return Bottom - Top; } }
            public int Width { get { return Right - Left; } }
            public Size Size { get { return new Size(Width, Height); } }

            public Point Location { get { return new Point(Left, Top); } }

            // Handy method for converting to a System.Drawing.Rectangle
            public Rectangle ToRectangle()
            { return Rectangle.FromLTRB(Left, Top, Right, Bottom); }

            public static RECT FromRectangle(Rectangle rectangle)
            {
                return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
            }

            public override int GetHashCode()
            {
                return Left ^ ((Top << 13) | (Top >> 0x13))
                  ^ ((Width << 0x1a) | (Width >> 6))
                  ^ ((Height << 7) | (Height >> 0x19));
            }

            #region Operator overloads

            public static implicit operator Rectangle(RECT rect)
            {
                return rect.ToRectangle();
            }

            public static implicit operator RECT(Rectangle rect)
            {
                return FromRectangle(rect);
            }

            public override string ToString()
            {

                return String.Format("RECT {{X={0},Y={1},Width={2},Height={3}}}", Left, Top, Width, Height);
            }

            #endregion
        }

        // For Windows Mobile, replace user32.dll with coredll.dll
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        //HHTaskBar
        //Shell_TrayWnd
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);


        

        internal static void FullScreen(ConfiguredMockScreens mockScreens)
        {

            //throw new Exception(mockScreens.UseWorkingArea.ToString());

            IntPtr fw = GetForegroundWindow();
            int lngStyle = GetWindowLong(fw, GWL_STYLE);
            int lngStyle2 = GetWindowLong(fw, GWL_EXSTYLE);


            if (!originalWinLongs.ContainsKey(fw))
            {
                originalWinLongs.Add(fw, lngStyle);
            }
            if (!originalWinLongs2.ContainsKey(fw))
            {
                originalWinLongs2.Add(fw, lngStyle2);
            }



            WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
            GetWindowPlacement(fw, ref wp);

            if (!originalWindowPlacements.ContainsKey(fw))
            {
                originalWindowPlacements.Add(fw, wp);
            }

            lngStyle = (int)(lngStyle & ~WS_MAXIMIZEBOX);
            lngStyle = (int)(lngStyle & ~0xC00000);

            System.Drawing.Rectangle vSize = Rectangle.Empty;


            int x0 = Int32.MaxValue;
            int y0 = Int32.MaxValue;
            int x1 = Int32.MinValue;
            int y1 = Int32.MinValue;

            foreach (KeyValuePair<string, MockScreen> kvp in mockScreens)
            {
                //System.Windows.Forms.SystemInformation.WorkingArea;

                Rectangle cBounds = kvp.Value.Bounds;//mockScreens.UseWorkingArea ? kvp.Value.WorkingArea : kvp.Value.Bounds;

                if (kvp.Value.Horizontal)
                {
                    x0 = Math.Min(x0, cBounds.X);
                    x1 = Math.Max(x1, cBounds.X + cBounds.Width);

                }
                if (kvp.Value.Vertical)
                {
                    y0 = Math.Min(y0, cBounds.Y);
                    y1 = Math.Max(y1, cBounds.Y + cBounds.Height);
                }
            }

            vSize = Rectangle.FromLTRB(x0, y0, x1, y1);









            if (!false && mockScreens.UseWorkingArea)
            {
                HandleRef tbarHref=new HandleRef(null,FindWindow("Shell_TrayWnd", string.Empty));
                RECT tbarRect = new RECT();
                GetWindowRect(tbarHref, out tbarRect);
                Console.WriteLine(tbarRect);
                //taskbar layout horiz/vert
                bool tbHorz=(Math.Abs((tbarRect.Width / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width) - 1) < 0.1f);

                if (tbHorz)
                {
                    Console.WriteLine("TB HORIZ");
                    if (Math.Abs(vSize.Y - tbarRect.Top) > Math.Abs(vSize.Y + vSize.Height - (tbarRect.Bottom)))
                    {
                        Console.WriteLine("UPPER");
                        //choose upper section
                        vSize.Height = tbarRect.Top;

                    }
                    else
                    {
                        Console.WriteLine("LOWER");
                        //choose lower section
                        vSize.Y = tbarRect.Bottom;
                        vSize.Height -= (tbarRect.Bottom);
                    }
                }
                else
                {
                    Console.WriteLine("TB VERT");
                    if (Math.Abs(vSize.X - tbarRect.Left) > Math.Abs(vSize.X + vSize.Width - (tbarRect.Right)))
                    {
                        Console.WriteLine("LEFT");
                        //choose upper section
                        vSize.Width = tbarRect.Left;

                    }
                    else
                    {
                        Console.WriteLine("RIGHT");
                        //choose lower section
                        vSize.X = tbarRect.Right;
                        vSize.Width -= (tbarRect.Right);
                    }





                }

                //if (System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.IntersectsWith(vSize))
                //{
                //    //System.Windows.Forms.Screen.PrimaryScreen.Bounds.Y,System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Y
                    
                //    //System.Windows.Forms.Screen.PrimaryScreen.Bounds
                //    //kvp.Value.Vertical


                //}
            }













            if (vSize.Height == 0 || vSize.Width == 0)
            {
                vSize = mockScreens.UseWorkingArea ? System.Windows.Forms.SystemInformation.WorkingArea : System.Windows.Forms.SystemInformation.VirtualScreen;
            }
            Console.WriteLine(vSize);


            //if (mockScreens.UseWorkingArea)
            //{
            //    //Console.WriteLine(System.Windows.Forms.SystemInformation.WorkingArea);
            //    //System.Windows.Forms.Screen.AllScreens[0].WorkingArea
            //    //Rectangle xx=Rectangle.Intersect(System.Windows.Forms.SystemInformation.WorkingArea, vSize);
            //    //Console.WriteLine(xx);
            //}
            //Console.WriteLine(System.Windows.Forms.SystemInformation.MaxWindowTrackSize);



            SetWindowLong(fw, GWL_STYLE, (IntPtr)lngStyle);
            SetWindowPos(fw, IntPtr.Zero, vSize.Left, vSize.Top, vSize.Width, vSize.Height, 0);
            
            SendMessage(fw, WM_Messages.WM_PAINT, IntPtr.Zero, IntPtr.Zero);
            SendMessage(fw, WM_Messages.WM_PRINT, IntPtr.Zero, IntPtr.Zero);



        }

        private static void RestoreOriginal()
        {

            IntPtr fw = GetForegroundWindow();
            ShowWindow(fw, SW_RESTORE);//return;
            if (originalWinLongs.ContainsKey(fw))
            {
                Console.WriteLine("SetWindowLong");
                SetWindowLong(fw, GWL_STYLE, (IntPtr)originalWinLongs[fw]);
                SetWindowLong(fw, GWL_EXSTYLE, (IntPtr)originalWinLongs2[fw]);
                originalWinLongs.Remove(fw);
                originalWinLongs2.Remove(fw);
            }

            if (originalWindowPlacements.ContainsKey(fw))
            {
                WINDOWPLACEMENT wp = originalWindowPlacements[fw];

                Console.WriteLine("SetWindowPlacement");
                Console.WriteLine(wp.rcNormalPosition);

                SetWindowPlacement(fw, ref wp);
                originalWindowPlacements.Remove(fw);
            }

            SendMessage(fw, WM_Messages.WM_PAINT, IntPtr.Zero, IntPtr.Zero);
            SendMessage(fw, WM_Messages.WM_PRINT, IntPtr.Zero, IntPtr.Zero);
            

        }



    }
}
